package com.zk.box.service;

import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 参考文章：
 * https://www.jianshu.com/p/7bf5dc61ca06/
 */
@Service
public class RedisService<K, V> {
    @Resource
    private RedisTemplate<K, V> redisTemplate;

    //region String 结构 API

    public void stringSet(K k, V v) {
        redisTemplate.opsForValue().set(k, v);
    }

    public void stringSet(K k, V v, Long timeout) {
        redisTemplate.opsForValue().set(k, v, timeout, TimeUnit.SECONDS);
    }

    public void stringSet(K k, V v, Long timeout, TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(k, v, timeout, timeUnit);
    }

    /**
     * Key 不存在 保存新增数据返回true
     * Key 存在 不做操作 返回false
     *
     * @param k
     * @param v
     * @return
     */
    public boolean stringSetIfAbsent(K k, V v) {
        return redisTemplate.opsForValue().setIfAbsent(k, v);
    }

    public boolean stringSetIfAbsent(K k, V v, Long timeout) {
        return redisTemplate.opsForValue().setIfAbsent(k, v, timeout, TimeUnit.SECONDS);
    }

    public boolean stringSetIfAbsent(K k, V v, Long timeout, TimeUnit timeUnit) {
        return redisTemplate.opsForValue().setIfAbsent(k, v, timeout, timeUnit);
    }

    public V stringGet(K k) {
        return redisTemplate.opsForValue().get(k);
    }

    /**
     * 保存多个 KV
     *
     * @param map
     */
    public void stringMultiSet(Map<? extends K, ? extends V> map) {
        redisTemplate.opsForValue().multiSet(map);
    }

    /**
     * 根据多个K获取多个V
     *
     * @param ks
     * @return
     */
    public List<V> stringMultiGet(Collection<K> ks) {
        return redisTemplate.opsForValue().multiGet(ks);
    }

    /**
     * 设置新V，返回旧V
     *
     * @param k
     * @param v
     * @return
     */
    public V stringGetAndSet(K k, V v) {
        return redisTemplate.opsForValue().getAndSet(k, v);
    }

    /**
     * 计数 整数为增加，负数为减
     *
     * @param k
     * @param v
     * @return
     */
    public Long stringIncrement(K k, Long v) {
        return redisTemplate.opsForValue().increment(k, v);
    }

    public Double stringIncrement(K k, Double v) {
        return redisTemplate.opsForValue().increment(k, v);
    }

    //endregion

    //region List 结构 API

    /**
     * 偏移开始和停止是基于零的索引
     *
     * @param key
     * @param start 0
     * @param end   -1
     * @return
     */
    public List<V> listRange(K key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    public Long listSize(K key) {
        return redisTemplate.opsForList().size(key);
    }

    public Long listLeftPush(K k, V v) {
        return redisTemplate.opsForList().leftPush(k, v);
    }

    public Long listLeftPushAll(K k, Collection<V> v) {
        return redisTemplate.opsForList().leftPushAll(k, v);
    }

    public Long listRightPush(K k, V v) {
        return redisTemplate.opsForList().rightPush(k, v);
    }

    public Long listRightPushAll(K k, Collection<V> v) {
        return redisTemplate.opsForList().rightPushAll(k, v);
    }

    public V listIndex(K key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    /**
     * 弹出最左边的元素，弹出之后该值在列表中将不复存在
     *
     * @param key
     * @return
     */
    public V listLeftPop(K key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    /**
     * 移出并获取列表的第一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
     *
     * @param key
     * @param timeout
     * @param unit
     * @return
     */
    public V listLeftPop(K key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().leftPop(key, timeout, unit);
    }

    public V listRightPop(K key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    public V listRightPop(K key, long timeout, TimeUnit unit) {
        return redisTemplate.opsForList().rightPop(key, timeout, unit);
    }

    //endregion

    //region Hash 结构 API
    public Map<K, V> hashEntries(K k) {
        return (Map<K, V>) redisTemplate.opsForHash().entries(k);
    }

    public void hashPut(K k, K hashK, V v) {
        redisTemplate.opsForHash().put(k, hashK, v);
    }

    public void hashPut(K k, Map<? extends K, ? extends V> v) {
        redisTemplate.opsForHash().putAll(k, v);
    }

    public Boolean hashPutIfAbsent(K key, K hashK, V v) {
        return redisTemplate.opsForHash().putIfAbsent(key, hashK, v);
    }

    /**
     * 传数组
     *
     * @param k
     * @param hashKs
     * @return
     */
    public Long hashDel(K k, Object... hashKs) {
        return redisTemplate.opsForHash().delete(k, hashKs);
    }

    public Boolean HashHasKey(K k, K hashK) {
        return redisTemplate.opsForHash().hasKey(k, hashK);
    }

    public V hashGet(K k, K hashK) {
        return (V) redisTemplate.opsForHash().get(k, hashK);
    }

    public Long hashIncrement(K key, K hashKey, long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }

    public Double hashIncrement(K key, K hashKey, Double delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }

    public K hashKeys(K k) {
        return (K) redisTemplate.opsForHash().keys(k);
    }

    public Long hashSize(K k) {
        return redisTemplate.opsForHash().size(k);
    }

    public List<V> hashValues(K key) {
        return (List<V>) redisTemplate.opsForHash().values(key);
    }
    //endregion

    //region set 结构 API
    public Set<V> setMembers(K k) {
        return redisTemplate.opsForSet().members(k);
    }

    /**
     * 随机获取key无序集合中的一个元素
     * @param key
     * @return
     */
    public V randomMember(K key){
        return redisTemplate.opsForSet().randomMember(key);
    }

    public Long setAdd(K k, V... v) {
        return redisTemplate.opsForSet().add(k, v);
    }

    public Long setRemove(K k, V... v) {
        return redisTemplate.opsForSet().remove(k, v);
    }

    public V setPop(K k) {
        return redisTemplate.opsForSet().pop(k);
    }

    public Long setSize(K k) {
        return redisTemplate.opsForSet().size(k);
    }

    /**
     * set集合中是否包含该V
     *
     * @param key
     * @param v
     * @return
     */
    public Boolean setIsMember(K key, V v) {
        return redisTemplate.opsForSet().isMember(key, v);
    }

    /**
     * 求两个Set集合的交集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<V> setIntersect(K key, K otherKey) {
        return redisTemplate.opsForSet().intersect(key, otherKey);
    }

    public Set<V> setIntersect(K key, Set<K> otherKey) {
        return redisTemplate.opsForSet().intersect(key, otherKey);
    }

    /**
     * 两个set集合的交集存储到 destKey集合中
     *
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long setIntersectAndStore(K key, K otherKey, K destKey) {
        return redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey);
    }

    public Long setIntersectAndStore(K key, Set<K> otherKey, K destKey) {
        return redisTemplate.opsForSet().intersectAndStore(key, otherKey, destKey);
    }

    /**
     * 求两个Set集合的并集
     *
     * @param key
     * @param otherKey
     * @return
     */
    public Set<V> setUnion(K key, K otherKey) {
        return redisTemplate.opsForSet().union(key, otherKey);
    }

    public Set<V> setUnion(K key, Set<K> otherKey) {
        return redisTemplate.opsForSet().union(key, otherKey);
    }

    /**
     * 两个set集合的并集存储到 destKey集合中
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long setUnionAndStore(K key, K otherKey, K destKey){
        return redisTemplate.opsForSet().unionAndStore(key,otherKey,destKey);
    }

    public Long setUnionAndStore(K key, Set<K> otherKey, K destKey){
        return redisTemplate.opsForSet().unionAndStore(key,otherKey,destKey);
    }

    /**
     * 求两个Set集合的差集
     * @param key
     * @param otherKey
     * @return
     */
    public Set<V> setDifference(K key, K otherKey){
        return redisTemplate.opsForSet().difference(key,otherKey);
    }
    public Set<V> setDifference(K key, Set<K> otherKey){
        return redisTemplate.opsForSet().difference(key,otherKey);
    }

    /**
     * 两个set集合的差集存储到 destKey集合中
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long setDifferenceAndStore(K key, K otherKey, K destKey){
        return redisTemplate.opsForSet().differenceAndStore(key,otherKey,destKey);
    }

    public Long setDifferenceAndStore(K key, Set<K> otherKey, K destKey){
        return redisTemplate.opsForSet().differenceAndStore(key,otherKey,destKey);
    }

    //endregion

    //region zset 结构 API
    /**
     * 新增一个有序集合，存在的话为true，不存在的话为false
     * @param key
     * @param value
     * @param score
     * @return
     */
    public Boolean zsetAdd(K key, V value, double score){
        return redisTemplate.opsForZSet().add(key,value,score);
    }

    public Long zsetAdd(K key, Set<ZSetOperations.TypedTuple<V>> tuples){
        return redisTemplate.opsForZSet().add(key,tuples);
    }

    public Long zsetRemove(K key, V v){
        return redisTemplate.opsForZSet().remove(key,v);
    }

    /**
     * 增加元素的score值，并返回增加后的值
     * @param key
     * @param value
     * @param delta
     * @return
     */
    public Double zsetIncrementScore(K key, V value, double delta){
        return redisTemplate.opsForZSet().incrementScore(key,value,delta);
    }

    /**
     * 返回有序集中指定成员的排名，其中有序集成员按分数值递增(从小到大)顺序排列
     * @param key
     * @param v
     * @return
     */
    public Long zsetRank(K key, V v){
        return redisTemplate.opsForZSet().rank(key,v);
    }

    /**
     * 返回有序集中指定成员的排名，其中有序集成员按分数值递减(从大到小)顺序排列
     * @param key
     * @param v
     * @return
     */
    public Long zsetReverseRank(K key, V v){
        return redisTemplate.opsForZSet().reverseRank(key,v);
    }

    /**
     * 通过 索引 区间返回有序集合成指定区间内的成员，其中有序集成员按分数值递增(从小到大)顺序排列
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<V> zsetRange(K key, long start, long end){
        return redisTemplate.opsForZSet().range(key,start,end);
    }

    public Set<V> zsetReverseRange(K key, long start, long end){
        return redisTemplate.opsForZSet().reverseRange(key,start,end);
    }

    /**
     * 通过索引区间返回有序集合成指定区间内的成员对象，其中有序集成员按分数值递增(从小到大)顺序排列
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<V>> zsetRangeWithScores(K key, long start, long end){
        return redisTemplate.opsForZSet().rangeWithScores(key,start,end);
    }

    /**
     * 通过分数返回有序集合指定区间内的成员，其中有序集成员按分数值递增(从小到大)顺序排列
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<V> zsetRangeByScore(K key, double min, double max){
        return redisTemplate.opsForZSet().rangeByScore(key,min,max);
    }

    /**
     * 通过分数返回有序集合指定区间内的成员，并在索引范围内，其中有序集成员按分数值递增(从小到大)顺序排列
     * @param key
     * @param min
     * @param max
     * @param offset
     * @param count
     * @return
     */
    public Set<V> zsetRangeByScore(K key, double min, double max, long offset, long count){
        return redisTemplate.opsForZSet().rangeByScore(key,min,max,offset,count);
    }

    /**
     * 通过分数返回有序集合指定区间内的成员对象，其中有序集成员按分数值递增(从小到大)顺序排列
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<ZSetOperations.TypedTuple<V>> zsetRangeByScoreWithScores(K key, double min, double max){
        return redisTemplate.opsForZSet().rangeByScoreWithScores(key,min,max);
    }

    /**
     * 通过索引区间返回有序集合成指定区间内的成员对象，其中有序集成员按分数值递减(从大到小)顺序排列
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<V>> zsetReverseRangeWithScores(K key, long start, long end){
        return redisTemplate.opsForZSet().reverseRangeWithScores(key,start,end);
    }

    /**
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Set<V> zsetReverseRangeByScore(K key, double min, double max){
        return redisTemplate.opsForZSet().reverseRangeByScore(key,min,max);
    }

    public Set<V> reverseRangeByScore(K key, double min, double max, long offset, long count){
        return redisTemplate.opsForZSet().reverseRangeByScore(key,min,max,offset,count);
    }

    public Set<ZSetOperations.TypedTuple<V>> zsetReverseRangeByScoreWithScores(K key, double min, double max){
        return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(key,min,max);
    }

    /**
     * 通过分数返回有序集合指定区间内的成员个数
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Long zsetCount(K key, double min, double max){
        return redisTemplate.opsForZSet().count(key,min,max);
    }

    public Long zsetSize(K key){
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * 获取指定成员的score值
     * @param key
     * @param v
     * @return
     */
    public Double zsetScore(K key, V v){
        return redisTemplate.opsForZSet().score(key,v);
    }

    /**
     * 移除指定索引位置的成员，其中有序集成员按分数值递增(从小到大)顺序排列
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Long zsetRemoveRange(K key, long start, long end){
        return redisTemplate.opsForZSet().removeRange(key,start,end);
    }

    /**
     * 根据指定的score值得范围来移除成员
     * @param key
     * @param min
     * @param max
     * @return
     */
    public Long zsetRemoveRangeByScore(K key, double min, double max){
        return redisTemplate.opsForZSet().removeRangeByScore(key,min,max);
    }

    /**
     * 计算给定的一个有序集的并集，并存储在新的 destKey中，key相同的话会把score值相加
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zsetUnionAndStore(K key, K otherKey, K destKey){
        return redisTemplate.opsForZSet().unionAndStore(key,otherKey,destKey);
    }

    /**
     * 计算给定的多个有序集的并集，并存储在新的 destKey中
     * @param key
     * @param otherKeys
     * @param destKey
     * @return
     */
    public Long zsetUnionAndStore(K key, Set<K> otherKeys, K destKey){
        return redisTemplate.opsForZSet().unionAndStore(key,otherKeys,destKey);
    }

    /**
     * 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zsetIntersectAndStore(K key, K otherKey, K destKey){
        return redisTemplate.opsForZSet().intersectAndStore(key,otherKey,destKey);
    }
    public Long zsetIntersectAndStore(K key, Collection<K> otherKeys, K destKey){
        return redisTemplate.opsForZSet().intersectAndStore(key,otherKeys,destKey);
    }

    //endregion


}
